package com.xishu.cash;

import com.xishu.config.Config;
import com.xishu.config.Constant;
import com.xishu.entity.order.Order;
import com.xishu.util.*;
import net.sf.json.JSONArray;
import net.sf.json.JSONObject;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ContentType;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.nio.charset.Charset;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 请用以下线上线下测试账号
 * SID#1000007081 (Alipay online, wechat, FPS, easylink二清)
 * Code: 8AF5AA6607A44CA7AB76A7DC2634A0A3
 * Key: AB75609A5A6A4E1B98BCF6B4CB91CF5A
 * <p>
 * SID#1000005700 (Alipay offline, FPS, easylink二清)
 * code：13D92BE5C68B43A7B4DDB94CAB077E02
 * key：787F69C79B9E45C1A124AB9AD742C0C2
 * 接口地址：
 * https://openapi-hk.qfapi.com
 * 接口文档：
 * https://doc.qfapi.com
 */
public class QfPayClient implements Tools {
    private static Logger logger = LoggerFactory.getLogger(QfPayClient.class);
    public static final String URL = "https://openapi-hk.qfapi.com";
    public static final String TEST_URL = "https://openapi-hk.qfapi.com";
    private Config config = Config.getInstance();

    public String getUrl() {
        if (Config.getInstance().isAliyun()) {
            return TEST_URL;
        } else {
            return URL;
        }
    }

    private String appCode;

    private String key;

    private String sid;

    public QfPayClient(String code, String key, String sid) {
        this.appCode = code;
        this.key = key;
        this.sid = sid;
    }

    public QfPayClient() {
    }

    /**
     * 请求接口
     *
     * @param url
     * @param headers
     * @param paramsMap
     * @return
     */
    public String post(String url, Map<String, String> headers, Map<String, String> paramsMap) {
        try {
            HttpClient httpClient = new DefaultHttpClient();
            HttpPost httpPost = new HttpPost(url);

            //设置HTTP头
            if (headers != null) {
                for (Map.Entry<String, String> entry : headers.entrySet()) {
                    httpPost.setHeader(entry.getKey(), entry.getValue());
                }
            }

            //添加参数
            ArrayList<BasicNameValuePair> list = new ArrayList<>();
            paramsMap.forEach((key, value) -> list.add(new BasicNameValuePair(key, value)));
            logger.info("list is {}", JSONArray.fromObject(list));

            httpPost.setEntity(new UrlEncodedFormEntity(list, "UTF-8"));

            //执行
            HttpResponse httpResponse = httpClient.execute(httpPost);
            HttpEntity entity = httpResponse.getEntity();

            if (entity != null) {
                //状态，该状态可用于判断，此处略
                logger.info("resp_status:" + httpResponse.getStatusLine().getStatusCode());
                Charset respCharset = ContentType.getOrDefault(entity).getCharset();
                String respData = EntityUtils.toString(entity, respCharset);
                logger.info("respData is {}", respData);
                return respData;
            }
        } catch (IOException e) {
            logger.error("e", e);
        }

        return "";
    }

    /**
     * 发送命令
     */
    public String sendCmd(String uri, Map<String, String> paramMap) {
        Map<String, String> headerMap = new HashMap<>();
        headerMap.put("Content-Type", "application/x-www-form-urlencoded");
        headerMap.put("charset", "UTF-8");
        headerMap.put("X-QF-APPCODE", appCode);
        headerMap.put("X-QF-SIGNTYPE", "SHA256");
        String join = join(paramMap);
        String sign = sign(join + key);
        headerMap.put("X-QF-SIGN", sign);
        return post(getUrl() + uri, headerMap, paramMap);
    }

    /**
     * 将参数连接起来
     * <p>
     * 签名描述
     * 签名过程分为两步，首先是将原始参数按照规则拼接成一个字符串S1，然后再将S1根据签名算法生成签名字符串sign；SHA256算法和MD5算法均支持使用，sign_type参数也需要做对应的调整，推荐使用SHA256
     * <p>
     * 参数原始字符串的拼接规则：
     * <p>
     * 所有参数按照参数名的ASCII码顺序从小到大排序（字典序），使用URL键值对的方式，以&作为分隔符，拼接成字符串S1，字符串S1末尾拼接密钥key（如：k1=value1&k2=value2&k3=value3key）。
     *
     * @param map
     * @return
     */
    public String join(Map<String, String> map) {
        TreeMap<String, String> treeMap = new TreeMap<String, String>(map);

        List<String> paramList = treeMap.entrySet().stream().map(p -> {
            Map.Entry<String, String> entry = (Map.Entry<String, String>) p;
            return entry.getKey() + "=" + entry.getValue();
        }).collect(Collectors.toList());

        return String.join("&", paramList);
    }

    /**
     * 将内容生成签名，默认是SHA256
     *
     * @param content
     * @return
     */
    public String sign(String content) {
        return Sha256Util.sha256(content);
    }

    /**
     * 验证签名
     *
     * @param content
     * @param key
     * @return
     */
    public boolean verify(String content, String key, String sign) {
        String md5 = Md5.md5(content + key);
        return equalsIgnoreCase(md5, sign);
    }

    /**
     * 查询指定订单在QF的状态
     *
     * @param order
     * @return
     */
    public List<Order> listOrder(Order order) throws Exception {
        Long shopId = order.getShopId();
        Long orderId = order.getId();
        HashMap<String, String> paramMap = new HashMap<>();
        paramMap.put("out_trade_no", shopId + Constant.SEPERATOR_KEY + orderId);

        return null;
    }

    /**
     * 公共支付接口
     *
     * @return
     */
    public String pay() {
        return "";
    }

    /**
     * 通用支付
     *
     * @param order
     * @return
     */
    public Map<String, String> commonPay(Order order) {
        Map paramMap = new HashMap<String, String>();
        //订单支付金额

        paramMap.put("txamt", String.valueOf(showMoneyDouble(order.getUserCost() * 100).intValue()));

        //币种
        paramMap.put("txcurrcd", "HKD");
        //外部订单号
        paramMap.put("out_trade_no", order.getShopId() + Constant.SEPERATOR_KEY + order.getId());
        //txdtm
        paramMap.put("txdtm", DateUtil.format(System.currentTimeMillis(), "yyyy-MM-dd HH:mm:ss"));
        //商品名称标识
        paramMap.put("goods_name", order.getShopName() + Constant.SEPERATOR_KEY + order.getOrderNo());
        //签名方式
        paramMap.put("sign_type", "SHA256");
        //可选参数
        //支付宝钱包标示	pay_tag	否	String(40)	支付宝PC/WAP/APP支付专用，pay_tag=ALIPAYCN大陆支付宝钱包，默认不传和pay_tag=ALIPAYHK香港支付宝钱包
        //微信支付人民币标价	rmb_tag	否	String(1)	字符串，1位，值为Y，若交易传参币种txcurrcd=CNY，则必传此参数rmb_tag=Y
        logger.info("param map is {}", JSONObject.fromObject(paramMap));

        return paramMap;
    }

    /**
     * 付款码支付
     * 800208	微信反扫支付	WeChat Customer Presented QR Code Payment (CPM)
     * 800008	微信、支付宝﹑银联云閃付反扫支付	Customer Presented QR Code Payment (CPM) for WeChat, Alipay, UNIONPAY Quick Pass
     *
     * @param payCode 付款码
     * @return
     */
    public String paymentCodePay(String payCode, Order order, String payType) {
        Map<String, String> paramMap = commonPay(order);
        paramMap.put("pay_type", payType);
        paramMap.put("auth_code", payCode);
        String result = sendCmd("/trade/v1/payment", paramMap);

        return result;
    }

    /**
     * 退款
     *
     * @param order
     * @return
     */
    public String returnPayment(Order order) {
        Map<String, String> paramMap = commonPay(order);
        paramMap.put("syssn", order.getSyssn());
        //退款的时候，ID随机传就可以了
//        paramMap.put("out_trade_no", order.getShopId() + Constant.SEPERATOR_KEY + order.getId());
        paramMap.put("out_trade_no", MongoId.generateId());
        //具体退多少费用
        String returnCash = showMoneyDouble(showDouble(order.getUserCost() * 100)).intValue() + "";
        paramMap.put("txamt", returnCash);

        return sendCmd("/trade/v1/refund", paramMap);
    }

    /**
     * 查询订单
     *
     * @param order
     * @return
     */
    public String query(Order order) {
        Map<String, String> paramMap = commonPay(order);
        paramMap.put("syssn", order.getSyssn());

        String responseData = sendCmd("/trade/v1/query", paramMap);
        return responseData;
    }

    /**
     * 验签操作
     *
     * @param input
     * @param sign
     * @return
     */
    public boolean verify(String input, String sign) {
        logger.info("input is {}, sign is {}", input, sign);

        if (isEmpty(sign)) {
            logger.info("sign is null");
            return false;
        }

        return equals(sign(input + key), sign);
    }
}
